package com.tbit.uqbike.webmanager.controller;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageInfo;
import com.tbit.common.constant.MethodEnum;
import com.tbit.common.constant.ModuleEnum;
import com.tbit.common.entity.pojo.vo.ReturnBikeMsgLogVO;
import com.tbit.common.entity.view.PageResult;
import com.tbit.common.entity.view.Result;
import com.tbit.uqbike.annos.OperateLog;
import com.tbit.uqbike.constant.MapConstant;
import com.tbit.uqbike.constant.SiteTypeConstant;
import com.tbit.uqbike.constant.UserConstant;
import com.tbit.uqbike.enums.ParkPointType;
import com.tbit.uqbike.enums.SystemCodeEnum;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.business.Point;
import com.tbit.uqbike.object.pojo.BatchAddParkPoint;
import com.tbit.uqbike.object.pojo.ParkPoint;
import com.tbit.uqbike.object.pojo.ParkPointMachineStat;
import com.tbit.uqbike.object.pojo.dto.GetReturnBikePointDTO;
import com.tbit.uqbike.object.pojo.dto.ParkPointMachineStaDownloadDTO;
import com.tbit.uqbike.object.pojo.dto.SiteOperationDTO;
import com.tbit.uqbike.object.pojo.statistics.ExportParkPoint;
import com.tbit.uqbike.object.pojo.vo.ParkPointMachineStatChartVO;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.object.view.JsonResult;
import com.tbit.uqbike.service.base.CalibrateService;
import com.tbit.uqbike.service.base.ParkPointService;
import com.tbit.uqbike.webmanager.util.GeoUtil;
import com.tbit.uqbike.webmanager.util.ServletUtils;
import com.tbit.uqbike.webmanager.util.StringUtil;
import com.tbit.uqbike.webmanager.util.csv.annotations.AsyncDownload;
import com.tbit.uqbike.webmanager.util.excel.ExcelUtil;
import com.tbit.utils.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 站点信息
 *
 * @author chenmengran
 * @Description: 站点
 * @date 2019\12\12 00129:47
 */
@Validated
@RestController
@RequestMapping(value = "/parkPoint", produces = BaseController.MEDIA_JSON_UTF8)
public class ParkPointController extends BaseController {

    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private ParkPointService parkPointService;

    /**
     * 新增站点
     *
     * @param parkPoint .
     * @param mapType   地图类型 0:原始 2:高德
     */
    @RequestMapping("/add")
    @OperateLog(module = ModuleEnum.MAINPARKPOINT, methods = MethodEnum.PARKPOINT_ADD)
    public JsonResult add(ParkPoint parkPoint, @NotNull(message = "ver.mapType.notNull") Integer mapType,
                          HttpServletRequest request) {
        /* 校验 */
        Assert.notNullAndHasLength(parkPoint.getName(), "ver.name.notNull");
        Assert.notNull(parkPoint.getParkPointType(), "ver.parkPointType.notNull");
        checkLength(parkPoint);
        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }

        if (!ParkPointType.Check(String.valueOf(parkPoint.getParkPointType()))) {
            throw new BaseException("未知停车点类型");
        }
        LoginInfo loginInfo = getLogin(request);
        /**经纬度校准*/
        calibrateService.calParkPoint(parkPoint, mapType, MapConstant.MAP_ORI);
        ParkPoint parkPointDB = parkPointService.insert(parkPoint, loginInfo);

        return JsonResult.ok(parkPointDB);
    }

    /**
     * 编辑站点
     *
     * @param parkPoint .
     * @param mapType   地图类型 0:原始 2:高德
     */
    @RequestMapping("/edit")
    @OperateLog(module = ModuleEnum.MAINPARKPOINT, methods = MethodEnum.PARKPOINT_EDIT)
    public JsonResult edit(ParkPoint parkPoint, @NotNull(message = "ver.mapType.notNull") Integer mapType, HttpServletRequest request) {
        /* 校验 */
        Assert.notNullAndHasLength(parkPoint.getName(), "ver.name.notNull");
        Assert.notNull(parkPoint.getParkPointType(), "ver.parkPointType.notNull");
        checkLength(parkPoint);
        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }

        if (!ParkPointType.Check(String.valueOf(parkPoint.getParkPointType()))) {
            throw new BaseException("未知停车点类型");
        }
        LoginInfo loginInfo = getLogin(request);
        if (loginInfo == null) {
            throw new BaseException(SystemCodeEnum.LOGIN_EXPIRED);
        }
        /**经纬度校准*/
        calibrateService.calParkPoint(parkPoint, mapType, MapConstant.MAP_ORI);
        ParkPoint parkPointDB = parkPointService.update(parkPoint, loginInfo);

        return JsonResult.ok(parkPointDB);
    }

    /**
     * 删除站点
     *
     * @param parkPointId 站点id
     */
    @RequestMapping("/del")
    @OperateLog(module = ModuleEnum.MAINPARKPOINT, methods = MethodEnum.PARKPOINT_DEL)
    public JsonResult del(@NotNull(message = "ver.parkPointId.notNull") Integer parkPointId, HttpServletRequest request) {
        LoginInfo loginInfo = getLogin(request);
        if (loginInfo == null) {
            throw new BaseException(SystemCodeEnum.LOGIN_EXPIRED);
        }
        parkPointService.del(parkPointId, loginInfo);

        return JsonResult.ok(null);
    }

    /**
     * 根据区域id查询
     *
     * @param accountId 区域id
     * @param mapType   地图类型 0:原始 2:高德
     * @param regionIds 分区id集合
     */
    @RequestMapping("/getByAccountId")
    public JsonResult getByAccountId(Integer accountId, Integer mapType, HttpServletRequest request, String regionIds,
                                     Integer level) {
        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }

        LoginInfo loginInfo = getLogin(request);
        if (loginInfo.type.equals(UserConstant.USER_AGENT)) {
            accountId = loginInfo.id;
        }
        Assert.notNull(accountId, "ver.accountId.notNull");
        List<Integer> regionIdList = StringUtil.getIntegerList(regionIds);
        List<ParkPoint> parkPointList = parkPointService.getByAccountId(accountId, loginInfo, regionIdList, level);
        if (parkPointList.size() > 0) {
            calibrateService.celibrateParkPoint(parkPointList, mapType);
        }
        return JsonResult.ok(parkPointList);
    }

    /**
     * 获取指定点附近的站点
     *
     * @param accountId 区域id
     * @param lo        经度
     * @param la        纬度
     * @param mapType   地图类型 0:原始 2:高德
     */
    @RequestMapping("/getNearByPoint")
    public JsonResult getNearByPoint(Integer accountId, @NotNull(message = "ver.lo.notNull") Double lo,
                                     @NotNull(message = "ver.la.notNull") Double la, Integer mapType,
                                     Integer radius,
                                     HttpServletRequest request) {
        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }

        LoginInfo info = getLogin(request);
        // 只有用户是代理商的时候允许为null
        if (null == accountId) {
            if (Objects.equals(info.type, UserConstant.USER_AGENT)) {
                accountId = info.id;
            } else {
                Assert.notNull(accountId, "ver.accountId.notNull");
            }
        }

        if (null == radius || radius <= 0) {
            radius = 500;
        }

        /**校准点*/
        Point p = new Point(lo, la);
        p = calibrateService.commonCalibrate(p, mapType, MapConstant.MAP_ORI);

        List<ParkPoint> parkPoints = parkPointService.getByAccountAndArea(accountId, p.getX(), p.getY(), radius);
        /*校准*/
        calibrateService.celibrateParkPoint(parkPoints, mapType);

        return JsonResult.ok(parkPoints);
    }

    /**
     * 站点分页
     *
     * @param accountId
     * @param mapType
     * @param machineNO
     * @param sortItem
     * @param name
     * @param regionIds
     * @param enable
     * @param rowCount
     * @param pageNO
     * @param rfid
     * @param siteType
     * @param rfidType      rfid状态 0:关闭 1:打开
     * @param railSpikeType 道钉状态
     * @param kickstandType 脚撑状态
     * @param cameraType    摄像头状态状态
     * @param request
     * @param level         站点等级
     * @return
     */
    @RequestMapping("/queryPage")
    public JsonResult queryPage(Integer accountId, Integer mapType, String machineNO, String sortItem, String name,
                                String regionIds, Integer enable, @RequestParam(defaultValue = "10") Integer rowCount,
                                @RequestParam(defaultValue = "1") Integer pageNO, String rfid, Integer siteType,
                                Integer rfidType, Integer railSpikeType, Integer kickstandType, Integer cameraType,
                                Integer level, HttpServletRequest request) {
        if (mapType == null) {
            mapType = MapConstant.MAP_GOOGLE;
        }
        LoginInfo loginInfo = getLogin(request);
        // 只有用户是代理商的时候允许为null
        if (loginInfo.type.equals(UserConstant.USER_AGENT)) {
            accountId = loginInfo.id;
        } else {
            Assert.notNull(accountId, "ver.accountId.notNull");
        }
        List<Integer> regionIdList = StringUtil.getIntegerList(regionIds);
        PageInfo<ParkPoint> pageInfo = parkPointService.queryPage(loginInfo, accountId, machineNO, rowCount, pageNO,
                sortItem, name, regionIdList, enable, rfid, siteType, rfidType, railSpikeType, kickstandType, cameraType
                , level);
        List<ParkPoint> parkPoints = pageInfo.getList();
        if (!CollectionUtils.isEmpty(parkPoints)) {
            calibrateService.celibrateParkPoint(parkPoints, mapType);
            //性能太差，去除
            //parkPoints.forEach(parkPoint -> parkPoint.setTotalNumberOfVehicles(parkMachineService.getByParkPointId(parkPoint.getParkPointId()).size()));
        }
        Long total = pageInfo.getTotal();
        return JsonResult.ok(parkPoints, total);
    }

    /**
     * 快速查询站点
     *
     * @param accountId 区域id
     * @param name      站点名称
     */
    @RequestMapping("/queryByName")
    public JsonResult queryPage(Integer accountId, String name, HttpServletRequest request) {
        LoginInfo loginInfo = getLogin(request);
        // 只有用户是代理商的时候允许为null
        if (loginInfo.type.equals(UserConstant.USER_AGENT)) {
            accountId = loginInfo.id;
        } else {
            Assert.notNull(accountId, "ver.accountId.notNull");
        }
        List<ParkPoint> parkPoints = parkPointService.queryByName(loginInfo, accountId, name);
        return JsonResult.ok(parkPoints);
    }

    /**
     * 导出站点
     *
     * @param parkPoint .
     * @param mapType   地图类型 0:原始 2:高德
     * @param regionIds 分区id集合
     */
    @GetMapping("/export")
    public JsonResult export(ParkPoint parkPoint, HttpServletRequest request, HttpServletResponse response,
                             String regionIds, @RequestParam(defaultValue = "0") Integer mapType) {
        LoginInfo loginInfo = getLogin(request);
        // 只有用户是代理商的时候允许为null
        if (loginInfo.type.equals(UserConstant.USER_AGENT)) {
            parkPoint.setAccountId(loginInfo.id);
        } else {
            Assert.notNull(parkPoint.getAccountId(), "ver.accountId.notNull");
        }
        List<Integer> regionIdList = StringUtil.getIntegerList(regionIds);
        Map<String, Object> map = new HashMap<>();
        map.put("regionList", regionIdList);
        map.put("parkPoint", parkPoint);
        List<ExportParkPoint> exportParkPoints = parkPointService.selectParkPoint(map);

        exportParkPoints.forEach(v -> {
            if (MapConstant.MAP_ORI.equals(mapType)) {
                v.setExportPoints(v.getPoints());
            } else {
                v.setExportPoints(v.getPointsC());
            }
            if (v.getSiteType() == null) {
                v.setSiteType(0);
            }
            if (SiteTypeConstant.GENERAL_SITE.equals(v.getSiteType())) {
                v.setHighPrecisionAllowRange(0);
            }
        });

        ExcelUtil<ExportParkPoint> excelUtil = new ExcelUtil<>(ExportParkPoint.class);
        JsonResult jsonResult = excelUtil.exportExcel(exportParkPoints, "站点信息");
        return JsonResult.ok(jsonResult.getData());
    }

    /**
     * @描述 控制是否开启附近站点
     * @参数 pSwitch :控制是否开启附近站点，0开启，1关闭
     * @返回值
     * @创建人 hzw
     * @创建时间 2021/10/18
     * @修改人和其它信息
     */
    @RequestMapping("/switchParkPoint")
    public JsonResult switchParkPoint(Integer pSwitch, Integer parkPointId) {
        Assert.notNull(pSwitch, "ver.parkPointSwitch.notNull");
        Assert.notNull(parkPointId, "ver.parkPointId.notNull");
        parkPointService.switchParkPoint(pSwitch, parkPointId);
        return JsonResult.ok();
    }

    /**
     * 批量新增模板
     *
     * @return
     */
    @GetMapping("/getBatchAddTemplate")
    public Result getBatchAddTemplate() {
        ExcelUtil<BatchAddParkPoint> excelUtil = new ExcelUtil<>(BatchAddParkPoint.class);
        List<BatchAddParkPoint> list = new ArrayList<>();
        BatchAddParkPoint batchAddParkPoint = new BatchAddParkPoint();
        batchAddParkPoint.setSiteName("test");
        batchAddParkPoint.setAllowRange(50);
        batchAddParkPoint.setHighPrecisionAllowRange(0);
        batchAddParkPoint.setCapacity(0);
        batchAddParkPoint.setPoints("114.408613,30.453748;114.408912,30.454289;114.409212,30.453748;113.777498,30.223266;114.409212,30.111111333");
        batchAddParkPoint.setLevel("普通站点");
        list.add(batchAddParkPoint);
        return Result.success(excelUtil.exportExcel(list, "批量新增站点信息模板").getData());
    }

    /**
     * 站点运营统计
     *
     * @return result
     */
    @PostMapping("/machine/stat")
    public Result<PageResult<ParkPointMachineStat>> selectParkPointMachineStatPage(@Valid @RequestBody SiteOperationDTO siteOperationDTO,
                                                                                   HttpServletRequest request) {
        validateAccountPermissions(request, siteOperationDTO.getAccountId());
        Page<ParkPointMachineStat> pageResult = parkPointService.selectParkPointMachineStatPage(siteOperationDTO.getAccountId(),
                siteOperationDTO.getStartTime(), siteOperationDTO.getEndTime(), siteOperationDTO.getRegionIds(),
                siteOperationDTO.getParkPointIds(), siteOperationDTO.getOrderBy(), siteOperationDTO.getRowCount(),
                siteOperationDTO.getPageNO());
        return Result.successPage(siteOperationDTO.getPageNO(), siteOperationDTO.getPageNO(), (int) pageResult.getTotal(),
                pageResult.getRecords());
    }

    /**
     * 站点运营统计-剩余可租车辆折线图
     *
     * @param accountId    区域id
     * @param startTime    开始时间
     * @param endTime      结束时间
     * @param regionIds    分区id 多个分区用,拼接 示例1,2,3,4
     * @param parkPointIds 站点id 多个站点用,拼接 示例1,2,3,4
     * @return result
     */
    @GetMapping("/machine/stat/chart")
    public Result<List<ParkPointMachineStatChartVO>> selectParkPointMachineStatChart(@NotNull(message = "请选择运营区域") Integer accountId,
                                                                                     @NotNull(message = "请选择开始时间") String startTime,
                                                                                     @NotNull(message = "请选择结束时间") String endTime,
                                                                                     String regionIds, String parkPointIds,
                                                                                     HttpServletRequest request
    ) {
        long l = DateUtil.between(DateUtil.parseDate(startTime), DateUtil.parseDate(endTime), DateUnit.HOUR);
        if (l >= 25) {
            throw new BaseException("查询时间不能超过一天");
        }
        validateAccountPermissions(request, accountId);
        List<ParkPointMachineStatChartVO> resultList = parkPointService.selectParkPointMachineStatChart(accountId, startTime,
                endTime, regionIds, parkPointIds);
        return Result.success(resultList);
    }

    /**
     * 站点运营数据导出csv
     *
     * @param parkPointMachineStaDownloadDTO 导出类
     * @return 下载url
     */
    @PostMapping("/machine/stat/export")
    @AsyncDownload(defaultFileName = "站点运营数据统计")
    public Result<String> parkPointMachineStatExportCsv(@Valid @RequestBody ParkPointMachineStaDownloadDTO parkPointMachineStaDownloadDTO) {
        String url = parkPointService.parkPointMachineStatExportCsv(parkPointMachineStaDownloadDTO);
        return Result.success(url);
    }


    /**
     * 站点运营数据导出
     *
     * @param parkPointMachineStaDownloadDTO 导出参数
     * @return 文件名
     */
    @PostMapping("/machine/stat/exportExcel")
    public Result<Object> parkPointMachineStatExport(@Valid @RequestBody ParkPointMachineStaDownloadDTO parkPointMachineStaDownloadDTO) {
        return parkPointService.parkPointMachineStatExport(parkPointMachineStaDownloadDTO);
    }


    /**
     * 批量新增（站点导入）
     *
     * @param file      文件
     * @param accountId 运营区域id
     * @return
     */
    @PostMapping("/batchAdd")
    public Result batchAdd(@RequestParam("file") MultipartFile file, @NotNull Integer accountId,
                           @RequestParam(defaultValue = "0") Integer mapType, HttpServletRequest request) {
        LoginInfo loginInfo = getLogin(request);
        parkPointService.batchAdd(file, accountId, loginInfo, mapType);
        return Result.success();
    }

    /**
     * 获取区域下的还车点
     *
     * @return
     */
    @GetMapping("/getReturnBikePoint")
    public Result<List<ReturnBikeMsgLogVO>> getReturnBikePoint(@Valid GetReturnBikePointDTO dto) {
        List<ReturnBikeMsgLogVO> returnBikePoint = parkPointService.getReturnBikePoint(dto);
        return Result.success(returnBikePoint);
    }

    /**
     * 根据区域id获取区域的站点是否大于10个
     *
     * @param accountId 区域id
     * @return true 大于10个，false小于等于10个
     */
    @GetMapping("/isMaxParkPointCount")
    public Result<Boolean> isMaxParkPointCount(@NotNull Integer accountId) {
        return Result.success(parkPointService.isMaxParkPointCount(accountId));
    }

    /**
     * 根据围栏分页查询站点
     *
     * @param pageNum   当前页码
     * @param pageSize  每页显示的行数
     * @param accountId 区域id
     * @param pointsStr 高德围栏
     */
    @GetMapping("/getParkPointByPoints")
    public Result<PageResult<ParkPoint>> getParkPointByPoints(@NotNull Integer accountId,
                                                              @NotBlank(message = "请先圈选区域") String pointsStr,
                                                              String machineNO, String sortItem, String name,
                                                              String regionIds, Integer enable, String rfid, Integer siteType,
                                                              Integer rfidType, Integer railSpikeType, Integer kickstandType,
                                                              Integer cameraType, Integer level,
                                                              @NotNull Integer pageNum, @NotNull Integer pageSize) {

        List<Point> points = GeoUtil.getPoints(pointsStr);
        Point point = new Point();

        List<Integer> regionIdList = StringUtil.getIntegerList(regionIds);
        PageInfo<ParkPoint> pageInfo = parkPointService.queryPage(getLogin(ServletUtils.getRequest()), accountId, machineNO,
                Integer.MAX_VALUE, 1, sortItem, name, regionIdList, enable, rfid, siteType, rfidType, railSpikeType,
                kickstandType, cameraType, level);
        List<ParkPoint> parkPointListDb = pageInfo.getList();

        List<ParkPoint> parkPointList = parkPointListDb.stream()
                .filter(parkPoint -> {
                    if (parkPoint.getLoC() == null || parkPoint.getLaC() == null) {
                        return false;
                    }
                    point.setX(parkPoint.getLoC());
                    point.setY(parkPoint.getLaC());
                    return GeoUtil.IsPtInPoly(point, points);
                })
                .collect(Collectors.toList());

        List<List<ParkPoint>> partition = ListUtil.partition(parkPointList, pageSize);

        PageResult<ParkPoint> result = new PageResult<>();
        if (CollectionUtils.isEmpty(partition)) {
            result.setList(new ArrayList<>());
        } else {
            result.setList(partition.get(pageNum - 1));
        }
        result.setPage(pageNum);
        result.setSize(pageSize);
        result.setTotal((long) parkPointList.size());
        return Result.success(result);
    }

    /**
     * 根据站点id批量删除站点
     *
     * @param parkPointListStr 站点id集合 英文逗号拼接
     */
    @PostMapping("/batchDelParkPoint")
    public Result<String> batchDel(String parkPointListStr) {
        List<Integer> parkPointList = StringUtil.getIntegerList(parkPointListStr);
        parkPointService.batchDel(parkPointList);
        return Result.success();
    }

    /**
     * 校验站点名称和备注长度
     *
     * @param parkPoint
     */
    void checkLength(ParkPoint parkPoint) {
        if (StrUtil.isNotEmpty(parkPoint.getName()) && parkPoint.getName().length() > 100) {
            throw new BaseException("站点名称过长");
        } else if (StrUtil.isNotEmpty(parkPoint.getRemark()) && parkPoint.getRemark().length() > 255) {
            throw new BaseException("站点备注过长");
        }
    }
}
